﻿// <copyright file="AddInCatalog.cs" company="Anatoly Sereda">
// © 2009 Anatoly Sereda
//
// This source is subject to the Microsoft Public License.
// Please see the License.txt file for more information.
// All other rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// </copyright>
// <author>Anatoly Sereda</author>
// <email>neonnvr@live.com</email>
// <date>2009-08-04</date>

using System;
using System.AddIn.Hosting;
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Linq;
using System.Security;

namespace MafMefInterop
{
	public class AddInCatalog : ComposablePartCatalog, ICompositionElement
	{
		private string pipelineRoot;
		private Dictionary<AddInToken, Type> allTokens = new Dictionary<AddInToken, Type>();
		private List<Type> contracts = new List<Type>();
		private bool isNew = true;
		private List<ComposablePartDefinition> parts = new List<ComposablePartDefinition>();

		private AddInActivationPolicy activationPolicy = new AddInActivationPolicy(AddInSecurityLevel.Host);
		private Func<IDictionary<string, object>, AddInActivationPolicy> activationPolicySelector = null;

		public AddInCatalog()
			: this(".")
		{
		}

		public AddInCatalog(string pipelineRootFolderPath, params string[] addInFolderPaths)
		{
			if (pipelineRootFolderPath == null)
			{
				throw new ArgumentNullException("pipelineRootFolderPath");
			}

			this.pipelineRoot = pipelineRootFolderPath;
			this.AddInFolderPaths = addInFolderPaths.ToList();
			this.ActivationPolicy = new AddInActivationPolicy(AddInSecurityLevel.Host);
		}

		public override IQueryable<ComposablePartDefinition> Parts
		{
			get
			{
				this.Refresh();
				return this.parts.AsQueryable();
			}
		}

		public string DisplayName
		{
			get { throw new NotImplementedException(); }
		}

		public ICompositionElement Origin
		{
			get { throw new NotImplementedException(); }
		}

		public AddInActivationPolicy ActivationPolicy
		{
			get
			{
				return this.activationPolicy;
			}

			set
			{
				this.activationPolicy = value;
				this.activationPolicySelector = null;
			}
		}

		public Func<IDictionary<string, object>, AddInActivationPolicy> ActivationPolicySelector
		{
			get
			{
				return this.activationPolicySelector;
			}

			set
			{
				this.activationPolicySelector = value;
				this.activationPolicy = null;
			}
		}

		public Func<IDictionary<string, object>, bool> Validator { get; set; }

		public ICollection<string> AddInFolderPaths { get; private set; }

		public AddInCatalog AddContract(Type hostViewOfContract)
		{
			this.contracts.Add(hostViewOfContract);
			return this;
		}

		public AddInCatalog AddContract<THostView>()
		{
			this.AddContract(typeof(THostView));
			return this;
		}

		public AddInCatalog SetActivationPolicy(AddInEnvironment environment)
		{
			this.ActivationPolicy = new AddInActivationPolicy(environment);
			return this;
		}

		public AddInCatalog SetActivationPolicy(AddInSecurityLevel trustLevel)
		{
			this.ActivationPolicy = new AddInActivationPolicy(trustLevel);
			return this;
		}

		public AddInCatalog SetActivationPolicy(AppDomain target)
		{
			this.ActivationPolicy = new AddInActivationPolicy(target);
			return this;
		}

		public AddInCatalog SetActivationPolicy(PermissionSet permissions)
		{
			this.ActivationPolicy = new AddInActivationPolicy(permissions);
			return this;
		}

		public AddInCatalog SetActivationPolicy(AddInProcess process, AddInSecurityLevel level)
		{
			this.ActivationPolicy = new AddInActivationPolicy(process, level);
			return this;
		}

		public AddInCatalog SetActivationPolicy(AddInProcess process, PermissionSet permissionSet)
		{
			this.ActivationPolicy = new AddInActivationPolicy(process, permissionSet);
			return this;
		}

		public AddInCatalog SetActivationPolicy(AddInSecurityLevel trustLevel, string appDomainName)
		{
			this.ActivationPolicy = new AddInActivationPolicy(trustLevel, appDomainName);
			return this;
		}

		public AddInCatalog SetActivationPolicy(Func<IDictionary<string, object>, AddInActivationPolicy> selector)
		{
			this.ActivationPolicySelector = selector;
			return this;
		}

		public AddInCatalog SetValidator(Func<IDictionary<string, object>, bool> validator)
		{
			this.Validator = validator;
			return this;
		}

		private void Refresh()
		{
			this.FindAddIns();
			this.parts.Clear();
			foreach (KeyValuePair<AddInToken, Type> token in this.allTokens)
			{
				this.parts.Add(new AddInComposablePartDefinition(token.Key, token.Value, this));
			}
		}

		private void FindAddIns()
		{
			this.allTokens.Clear();

			this.RefreshStore();

			foreach (Type contract in this.contracts)
			{
				foreach (
				AddInToken token
				in AddInStore.FindAddIns(contract, this.pipelineRoot, this.AddInFolderPaths.ToArray()))
				{
					this.allTokens.Add(token, contract);
				}
			}
		}

		private void RefreshStore()
		{
			if (this.isNew)
			{
				AddInStore.Rebuild(this.pipelineRoot);
				this.isNew = false;
			}

			AddInStore.Update(this.pipelineRoot);
			foreach (string addInFolder in this.AddInFolderPaths)
			{
				AddInStore.UpdateAddIns(addInFolder);
			}
		}
	}
}
